use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::{Arc, RwLock};
use dashmap::DashMap;
use crate::{RedisConfig, RespFrame, Session, SimpleError};
use crate::cmd::del_cmd::*;
use crate::cmd::get_cmd::*;
use crate::cmd::set_cmd::*;
use crate::cmd::select_db_cmd::*;

pub fn parse_cmd(buf: String, db: Redis, sesseion_id: String, sessions: Arc<DashMap<String, Session>>) -> Result<RespFrame, String> {
    let fragments: Vec<&str> = buf.split("\r\n").collect();
    println!("fragments is {:?}", fragments);
    let binding = fragments[2].to_uppercase();
    let command = binding.as_str();

    println!("command is {:?}", command);

    let cmd = CmdType::from(command);

    println!("cmd is {:?}", cmd);
    match cmd {
        CmdType::NOT_SUPPORT => {
            Ok(RespFrame::Error(SimpleError::new("not support cmd")))
        }
        CmdType::DEL => {
            let cmd = DelCmd {};
            cmd.execute(fragments, db, sesseion_id, sessions)
        }
        CmdType::GET => {
            let cmd = GetCmd {};
            cmd.execute(fragments, db, sesseion_id, sessions)
        }
        CmdType::SET => {
            let cmd = SetCmd {};
            cmd.execute(fragments, db, sesseion_id, sessions)
        }

        CmdType::SELECT => {
            let cmd = SelectCmd {};
            cmd.execute(fragments, db, sesseion_id, sessions)
        }
    }
}


pub trait CmdStrategy {
    fn execute(&self, fragments: Vec<&str>, db: Redis, sesseion_id: String, sessions: Arc<DashMap<String, Session>>) -> Result<RespFrame, String>;
}

#[derive(Debug, Copy, Clone)]
pub enum CmdType {
    DEL,
    GET,
    SET,
    SELECT,
    NOT_SUPPORT,
}


impl From<&str> for CmdType {
    fn from(s: &str) -> Self {
        let binding = s.to_uppercase();
        let s = binding.as_str();
        match s {
            "DEL" => CmdType::DEL,
            "GET" => CmdType::GET,
            "SET" => CmdType::SET,
            "SELECT" => CmdType::SELECT,
            _ => CmdType::NOT_SUPPORT,
        }
    }
}

impl Debug for dyn CmdStrategy {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "SetCmd")
    }
}


#[derive(Clone, Debug)]
pub struct CmdHash {
    pub data: Arc<RwLock<HashMap<String, Box<dyn CmdStrategy>>>>,
}


impl CmdHash {
    pub fn new() -> Self {
        let cmd_hash = init_cmd();
        CmdHash {
            data: Arc::new(RwLock::new(cmd_hash))
        }
    }
}

fn init_cmd() -> HashMap<String, Box<dyn CmdStrategy>> {
    let mut cmd_hash: HashMap<String, Box<dyn CmdStrategy>> = HashMap::new();

    cmd_hash.insert("SET".to_string(), Box::new(SetCmd));
    cmd_hash.insert("GET".to_string(), Box::new(GetCmd));
    cmd_hash.insert("DEL".to_string(), Box::new(DelCmd));

    cmd_hash
}

#[derive(Debug, Clone)]
pub struct Redis {
    pub databases: Arc<Vec<RwLock<HashMap<String, String>>>>,
    pub redis_config: Arc<RedisConfig>,
}


const DB_NUM: usize = 16;
impl Redis {
    pub fn new(redis_config: RedisConfig) -> Self {
        let mut  databases = Vec::with_capacity(16);

        for _ in 0..DB_NUM {
            let db = RwLock::new(HashMap::new());

            databases.push(db);
        }

        Redis {
            databases: Arc::new(databases),
            redis_config: Arc::new(redis_config),
        }
    }
}


pub enum RedisData {
    RedisString()
}

